package com.usoft.thrift.client.proxy;

import java.io.Closeable;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import com.usoft.thrift.client.pool.PoolOperationCallBack;
import com.usoft.thrift.client.pool.ThriftPooledClientFactory;
import com.usoft.thrift.client.provider.ThriftServerAddressProvider;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.apache.thrift.TServiceClient;
import org.apache.thrift.TServiceClientFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;

/**
 * 客户端代理
 */
@SuppressWarnings({"unchecked", "rawtypes"})
public class ThriftServiceClientProxyFactory implements FactoryBean, InitializingBean, Closeable {

    private Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * 最大活跃连接数
     */
    private Integer maxTotal = 32;

    /**
     * ms,default 3 min,链接空闲时间
     * -1,关闭空闲检测
     */
    private Integer idleTime = 180000;
    private ThriftServerAddressProvider serverAddressProvider;

    private Object proxyClient;
    private Class<?> objectClass;

    /**
     * TServiceClient的对象池
     */
    private GenericObjectPool<TServiceClient> pool;

    private PoolOperationCallBack callback = new PoolOperationCallBack() {
        @Override
        public void create(TServiceClient client) {
            logger.info("create");
        }

        @Override
        public void destroy(TServiceClient client) {
            logger.info("destroy");
        }
    };

    public void setMaxTotal(Integer maxTotal) {
        this.maxTotal = maxTotal;
    }

    public void setIdleTime(Integer idleTime) {
        this.idleTime = idleTime;
    }

    public void setServerAddressProvider(
            ThriftServerAddressProvider serverAddressProvider) {
        this.serverAddressProvider = serverAddressProvider;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        // 加载Iface接口
        objectClass = classLoader.loadClass(serverAddressProvider.getService() + "$Iface");

        // 加载Client.Factory类
        Class<TServiceClientFactory<TServiceClient>> fi = (Class<TServiceClientFactory<TServiceClient>>) classLoader.
                loadClass(serverAddressProvider.getService() + "$Client$Factory");

        TServiceClientFactory<TServiceClient> clientFactory = fi.newInstance();
        ThriftPooledClientFactory pooledClientFactory = new ThriftPooledClientFactory(serverAddressProvider, clientFactory, callback);

        /**
         * client连接池的配置
         */
        GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
        poolConfig.setMaxTotal(maxTotal);
        poolConfig.setMaxIdle(1);
        poolConfig.setMinIdle(0);
        poolConfig.setMinEvictableIdleTimeMillis(idleTime);
        poolConfig.setTimeBetweenEvictionRunsMillis(idleTime * 2L);
        poolConfig.setTestOnBorrow(true);
        poolConfig.setTestOnReturn(false);
        poolConfig.setTestWhileIdle(false);

        pool = new GenericObjectPool<>(pooledClientFactory, poolConfig);
        proxyClient = Proxy.newProxyInstance(classLoader, new Class[]{objectClass}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 获取池化的client对象
                TServiceClient client = pool.borrowObject();
                boolean flag = true;
                try {
                    return method.invoke(client, args);
                } catch (Exception e) {
                    flag = false;
                    throw e;
                } finally {
                    if (flag) {
                        pool.returnObject(client);
                    } else {
                        pool.invalidateObject(client);
                    }
                }
            }
        });
    }

    @Override
    public Object getObject() throws Exception {
        return proxyClient;
    }

    @Override
    public Class<?> getObjectType() {
        return objectClass;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    @Override
    public void close() {
        if (pool != null) {
            try {
                pool.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        if (serverAddressProvider != null) {
            try {
                serverAddressProvider.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
